#ifndef MONO_COMMON_THREAD_LOCAL_H_
#define MONO_COMMON_THREAD_LOCAL_H_
#include <pthread.h>


namespace mono {

/*!
*this a tempalte class for thread local variable, 
* it will auto release on ThreadLocal object release;
* some thing that your can use __thread instad of this class 
* but it will not release by auto.
*/

template<typename T>
class ThreadLocal {
public:

	/*!
	*Create a ThreadLocal obj
	*/
	ThreadLocal()
	{
		pthread_key_create(&_mkey, &ThreadLocal::Destroy);
	}


	~ThreadLocal()
	{
		//pthread_key_delete(_mkey);
	}

	T& GetValue()
	{
		T* local_thread_value = static_cast<T*>(pthread_getspecific(_mkey));
		if (NULL == local_thread_value) {
			T* newObj = InitialValue();
			pthread_setspecific(_mkey, newObj);
			local_thread_value = newObj;
		}
		return *local_thread_value;
	}

	typedef T* InstanceCreator(void* data);

	T& GetValue(InstanceCreator* creator, void* data)
	{
		T* local_thread_value = static_cast<T*>(pthread_getspecific(_mkey));
		if (NULL == local_thread_value) {
			T* newObj = creator(data);
			pthread_setspecific(_mkey, newObj);
			local_thread_value = newObj;
		}
		return *local_thread_value;
	}

	void SetValue(const T& v)
	{
		T& t = GetValue();
		t = v;
	}
private:
	pthread_key_t _mkey;
	static void Destroy(void *x)
	{
		T* obj = static_cast<T*>(x);
		delete obj;
	}
	T* InitialValue()
	{
		return new T;
	}
	};

	template<typename T>
	class ThreadLocal<T*>
	{
		private:
			pthread_key_t _mkey;
			static void Destroy(void *x)
			{
				T* obj = static_cast<T*>(x);
				delete obj;
			}
			static void Empty(void *x)
			{

			}
			T** InitialValue()
			{
				T** t = new (T*);
				*t = NULL;
				return t;
			}
		public:
			ThreadLocal(bool destroy = true)
			{
				pthread_key_create(&_mkey,
				        destroy ? &ThreadLocal::Destroy : &ThreadLocal::Empty);
			}

			~ThreadLocal()
			{
				pthread_key_delete(_mkey);
			}

			T*& GetValue()
			{
				T** local_thread_value = static_cast<T**>(pthread_getspecific(
				        _mkey));
				if (NULL == local_thread_value) {
					T** newObj = InitialValue();
					pthread_setspecific(_mkey, newObj);
					local_thread_value = newObj;
				}
				return *local_thread_value;
			}

			void SetValue(T* v)
			{
				T*& t = GetValue();
				t = v;
			}
	};
}

#endif 
